package com.lagou.filter;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Activate(group = {CommonConstants.CONSUMER})
public class RsFilter implements Filter, Runnable {

    private ConcurrentHashMap<String, List<MethodStat>> methodRsMap = new ConcurrentHashMap();

    public RsFilter() {
        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(this, 5, 5, TimeUnit.SECONDS);
        Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(() -> {
            final long downLimit = System.currentTimeMillis() - 60000;
            final long upLimit = System.currentTimeMillis();
            methodRsMap.forEach((k, v) -> {
                Iterator<MethodStat> iterator = v.iterator();
                while (iterator.hasNext()) {
                    MethodStat methodStat = iterator.next();
                    if (methodStat.getStartTime() < downLimit || methodStat.getEndTime() > upLimit) {
                        iterator.remove();
                    }
                }
            });
        }, 5, 5, TimeUnit.SECONDS);
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        long start = System.currentTimeMillis();
        Result result = invoker.invoke(invocation);
        long end = System.currentTimeMillis();
        List<MethodStat> methodStats = methodRsMap.getOrDefault(invocation.getMethodName(), new ArrayList<>());
        methodStats.add(new MethodStat(invocation.getMethodName(), end - start, start, end));
        methodRsMap.put(invocation.getMethodName(), methodStats);
        return result;
    }

    public long getTp(String method, double rate) {
        long downLimit = System.currentTimeMillis() - 60000;
        long upLimit = System.currentTimeMillis();
        List<MethodStat> methodStats = methodRsMap.get(method);
        List<MethodStat> stats = methodStats.stream().filter(item -> item.getStartTime() >= downLimit && item.getEndTime() <= upLimit).sorted((m1, m2) -> Math.toIntExact(m1.getRs() - m2.getRs())).collect(Collectors.toList());
        int index = (int) (stats.size() * rate);
        return stats.get(index).getRs();
    }

    @Override
    public void run() {
        methodRsMap.forEach((k, v) -> {
            System.out.print(String.format("%s TP90 = %s毫秒 ", k, getTp(k, 0.9d)));
            System.out.print(String.format("%s TP99 = %s毫秒", k, getTp(k, 0.99d)));
            System.out.println();
        });
    }
}
